package ads.server;

import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

import org.apache.log4j.Logger;

import spread.SpreadException;
import spread.SpreadMessage;
import ads.exception.SpreadRemoteException;

public class SpreadRequestHandler implements InvocationHandler {
	private SpreadMember spreadMember;
	private final static double timeout = 1.5;;
	private static Long msgID = 0L;
	private static final Logger log = Logger
			.getLogger(SpreadRequestHandler.class.getName());

	public SpreadRequestHandler(SpreadMember spreadMember) {
		this.spreadMember = spreadMember;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		log.debug("spread remote invoke. method: " + method.toString()
				+ " args:" + args);
		String msgid;
		synchronized (msgID) {
			msgID++;
			msgid = spreadMember.privateServerGroup.toString() + msgID;
		}
		Serializable[] as = null;
		if (args != null) {
			as = parseArgs(args);
		}

		SpreadRequestMessage data = new SpreadRequestMessage(msgid, method
				.getName(), as);
		SpreadMessage message = new SpreadMessage();
		message.addGroup(spreadMember.config.getGroupName());
		message.setFifo();
		message.setObject(data);
		spreadMember.connection.multicast(message);

		if (method.getReturnType().equals(Void.TYPE)) {
			log.debug("method: " + method.toString()
					+ "has no return object. remote invoke finish.");
			return null;
		}
		log.debug("spread remote invoke was sent,waiting response");

		SpreadResponseMessage msg = SpreadMessageQueque.takeMsg(msgid);
		double waitSecont = 0;
		while (msg == null && waitSecont < timeout) {
			Thread.sleep(100L);
			waitSecont += 0.2;
			msg = SpreadMessageQueque.takeMsg(msgid);
		}

		if (msg != null) {
			Object result = msg.getResult();
			log.debug("spread remote invoke was answnered.result:" + result);
			if (result instanceof Throwable) {
				throw (Throwable) result;
			}
			return result;
		} else if (waitSecont >= timeout) {
			String errMsg = "Timeout,no response of invoker after " + timeout
					+ " seconds ";
			log.info(errMsg);
			 throw new SpreadRemoteException(errMsg);
		}
		return null;
	}

	private boolean isMyResponse(Long msgid, SpreadMessage message) {
		if (!message.isRegular()) {
			return false;
		}
		try {

			if (message.getObject() instanceof SpreadResponseMessage) {
				SpreadResponseMessage msg = (SpreadResponseMessage) message
						.getObject();
				if (msg.getId().equals(msgid)) {
					return true;
				}
			}
		} catch (SpreadException e) {
			e.printStackTrace();
		}
		return false;
	}

	private Serializable[] parseArgs(Object[] args)
			throws SpreadRemoteException {
		Serializable[] serializableArgs = new Serializable[args.length];

		for (int i = 0; i < args.length; i++) {
			Object arg = args[i];
			if (arg instanceof Serializable) {
				serializableArgs[i] = (Serializable) args[i];
			} else {
				throw new SpreadRemoteException("argument type "
						+ arg.getClass().getCanonicalName()
						+ " is not Serializable");
			}
		}

		return serializableArgs;
	}
}
